package com.webchat.ugc.service.moment;


import com.webchat.common.constants.MomentConstants;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.enums.messagequeue.MessageQueueEnum;
import com.webchat.common.exception.BusinessException;
import com.webchat.common.service.RedisService;
import com.webchat.common.service.messagequeue.producer.MessageQueueProducer;
import com.webchat.common.util.JsonUtil;
import com.webchat.domain.dto.queue.MomentPublishMessageDTO;
import com.webchat.domain.vo.request.MomentSaveOrUpdateVO;
import com.webchat.domain.vo.response.moment.MomentDetailVO;
import com.webchat.domain.vo.response.moment.MomentLinkVO;
import com.webchat.domain.vo.response.moment.MomentMediaVO;
import com.webchat.domain.vo.response.moment.MomentVO;
import com.webchat.ugc.repository.dao.IMomentDAO;
import com.webchat.ugc.repository.entity.MomentEntity;
import com.webchat.ugc.repository.entity.MomentLinkEntity;
import com.webchat.ugc.repository.entity.MomentMediaEntity;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class MomentService {


    @Autowired
    private IMomentDAO momentDAO;
    @Autowired
    private MomentMediaService momentMediaService;
    @Autowired
    private MomentLinkService momentLinkService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private MessageQueueProducer<MomentPublishMessageDTO, Long> messageQueueProducer;
    @Autowired
    private RedissonClient redissonClient;


    /**
     * 朋友圈动态发布
     *
     * @param momentSaveOrUpdate
     * @return
     */
    @Transactional
    public Long publish(MomentSaveOrUpdateVO momentSaveOrUpdate) {

        /**
         * 1. 持久化
         * 1.1 朋友圈动态数据持久化
         * 1.2 动态资源持久化
         * 1.3 链接数据持久化
         */
        MomentEntity moment = this.convert(momentSaveOrUpdate);
        moment = momentDAO.save(moment);
        Long momentId = moment.getId();
        // 持久化图片、视频媒体资源数据
        momentMediaService.saveMomentMedia(momentId, momentSaveOrUpdate);
        // 持久化朋友圈动态连接数据
        momentLinkService.saveMomentLink(momentId, momentSaveOrUpdate);
        /**
         * 2. 动态详情缓存（redis）
         */
        MomentVO momentVO = this.refreshMomentCache(momentId);
        /**
         * 3. mq
         * 3.1
         * 图片、视频媒体资源信息提取
         * 连接解析
         * ip归属地解析
         * 3.2 大模型内容审核
         * 3.3 刷新/修改动态状态
         * 3.4 写扩散（把当前动态写入到所有粉丝时间线DB、Redis）
         */
        MomentPublishMessageDTO messageDTO = new MomentPublishMessageDTO();
        messageDTO.setMoment(momentVO);
        messageQueueProducer.send(MessageQueueEnum.QUEUE_MOMENT_PUBLISH, messageDTO);
        return moment.getId();
    }


    /**
     * 获取单条动态详情
     *
     * @param momentId
     * @return
     */
    public MomentVO getMomentFromCache(Long momentId) {

        String cacheKey = this.momentBaseCacheKey(momentId);
        String cache = redisService.get(cacheKey);
        if (StringUtils.isNotBlank(cache)) {
            return JsonUtil.fromJson(cache, MomentVO.class);
        }
        // 主动刷新
        String lockKey = RedisKeyEnum.MOMENT_CACHE_REFRESH_LOCK.getKey(String.valueOf(momentId));
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock();
            // 双重检查
            if (StringUtils.isNotBlank(cache = redisService.get(cacheKey))) {
                return JsonUtil.fromJson(cache, MomentVO.class);
            }
            return this.refreshMomentCache(momentId);
        } catch (Exception e) {
            throw new BusinessException("数据加载失败");
        } finally {
            lock.unlock();
        }
    }

    /**
     * 批量查询动态详情
     *
     * @param momentIds
     * @return
     */
    public List<MomentVO> batchGetMomentFromCache(List<Long> momentIds) {

        if (CollectionUtils.isEmpty(momentIds)) {
            return Collections.emptyList();
        }

        List<MomentVO> moments = new ArrayList<>();

        List<String> keys = momentIds.stream().map(this::momentBaseCacheKey).collect(Collectors.toList());
        List<String> caches = redisService.mget(keys);
        for (int i = 0; i < momentIds.size(); i++) {
            Long momentId = momentIds.get(i);
            String cache = caches.get(i);
            if (StringUtils.isNotBlank(cache)) {
                moments.add(JsonUtil.fromJson(cache, MomentVO.class));
            } else {
                // TODO 这里可以优化，不建议在循环中查库刷新缓存，可能存在性能瓶颈
                // 建议：这里批量收集一批缓存失败的ID，最后统一批量刷新
                moments.add(this.refreshMomentCache(momentId));
            }
        }
        return moments;
    }


    public MomentVO refreshMomentCache(Long momentId) {

        MomentEntity moment = momentDAO.findById(momentId).orElse(null);
        if (moment == null) {
            return null;
        }
        List<MomentMediaEntity> medias = null;

        if (moment.isIncludeImages()) {
            medias = momentMediaService.medias(momentId, MomentConstants.MediaType.IMAGE.getType());
        }
        if (moment.isIncludeVideo()) {
            medias = momentMediaService.medias(momentId, MomentConstants.MediaType.VIDEO.getType());
        }
        MomentLinkEntity link = null;
        if (moment.isIncludeLink()) {
            link = momentLinkService.getLink(momentId);
        }
        return this.refreshMomentCache(moment, medias, link);
    }


    private MomentVO refreshMomentCache(MomentEntity moment,
                                        List<MomentMediaEntity> momentMedias,
                                        MomentLinkEntity linkEntity) {
        if (moment == null) {
            return null;
        }
        MomentVO momentVO = this.convertVo(moment, momentMedias, linkEntity);
        String momentCacheKey = this.momentBaseCacheKey(moment.getId());
        redisService.set(momentCacheKey, JsonUtil.toJsonString(momentVO),
                RedisKeyEnum.MOMENT_BASE_CACHE.getExpireTime());
        return momentVO;
    }

    private MomentVO convertVo(MomentEntity moment,
                               List<MomentMediaEntity> momentMedias,
                               MomentLinkEntity linkEntity) {

        MomentVO momentVO = new MomentVO();
        BeanUtils.copyProperties(moment, momentVO);
        momentVO.setPublishTime(moment.getCreateDate().getTime());
        if (moment.isIncludeImages()) {
            List<MomentMediaVO> imagesVo = momentMedias.stream().map(img -> {
                MomentMediaVO image = new MomentMediaVO();
                BeanUtils.copyProperties(img, image);
                return image;
            }).collect(Collectors.toList());
            momentVO.setImages(imagesVo);
        }
        if (moment.isIncludeVideo()) {
            MomentMediaEntity video = momentMedias.get(0);
            MomentMediaVO videoVo = new MomentMediaVO();
            BeanUtils.copyProperties(video, videoVo);
            momentVO.setVideo(videoVo);
        }
        if (moment.isIncludeLink()) {
            MomentLinkVO linkVO = new MomentLinkVO();
            BeanUtils.copyProperties(linkEntity, linkVO);
            momentVO.setLink(linkVO);
        }
        return momentVO;
    }

    private String momentBaseCacheKey(Long momentId) {

        return RedisKeyEnum.MOMENT_BASE_CACHE.getKey(String.valueOf(momentId));
    }

    private MomentEntity convert(MomentSaveOrUpdateVO momentSaveOrUpdate) {
        MomentEntity moment;
        Long momentId = momentSaveOrUpdate.getId();
        if (momentId != null) {
            moment = momentDAO.findById(momentSaveOrUpdate.getId()).orElse(null);
            Assert.notNull(moment, "动态不存在");
            Assert.isTrue(ObjectUtils.equals(momentSaveOrUpdate.getAuthor(), moment.getAuthor()), "无操作权限");
        } else {
            moment = new MomentEntity();
            moment.setStatus(MomentConstants.MomentStatusEnum.NEW.getStatus());
            moment.setAuthor(momentSaveOrUpdate.getAuthor());
            moment.setIp(momentSaveOrUpdate.getIp());
            moment.setCreateDate(new Date());
        }
        moment.setContent(momentSaveOrUpdate.getContent());
        moment.setIncludeImages(momentSaveOrUpdate.includeImage());
        moment.setIncludeVideo(momentSaveOrUpdate.includeVideo());
        moment.setIncludeLink(momentSaveOrUpdate.includeLink());
        return moment;
    }
}
